﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BacteriaSimulation.Simulation
{
    public class SimulationEnvironment
    {
        protected SimulationEnvironment instance;

        public static readonly Double MAX_ENERGY = 30;
        public static readonly Double INITIAL_ENERGY = 25;
        public static readonly Double MAX_SPEED_COMPONENT = 60;
        //public static readonly Double INITIAL_WIDTH = 5;
        //public static readonly Double INITIAL_HEIGHT = 5;
        public static readonly Double EPSILON = 0.1;
        public static readonly Int32 SIMULATION_STEP_INTERVAL = 40;
        public static readonly Double ENERGY_INCREMENT = .2;
        //public static readonly Double WIDTH_INCREMENT = 1;
        //public static readonly Double HEIGHT_INCREMENT = 1;
        public static readonly Boolean DETECT_BACTERIA_COLLISION = true;
        public static readonly Double MUTATION_PROBABILITY = .08;

        public Double Width { get; set;  }
        public Double Height { get; set; }

        public Double MaxEnergy { get; set; }
        public Double InitialEnergy { get; set; }
        //public Double InitialWidth { get; set; }
        //public Double InitialHeight { get; set; }
        public Double Epsilon { get; set; }
        public Int32 SimulationStepInterval { get; set; }
        public Double EnergyIncrement { get; set; }
        //public Double WidthIncrement { get; set; }
        //public Double HeightIncrement { get; set; }
        public Boolean DetectBacteriaCollision { get; set; }
        public Double InitialEnergyResource { get; set; }
        public Double AvalaibleEnergyResource { get; set; }
        public Double MutationProbability { get; set; }
        public Boolean CapEnergy { get; set; }

        public ForceField ActiveForceField { get; set; }

        public SimulationEnvironment(Double width, Double height, Double initialEnergyResource, Double mutationProbability, Boolean capEnergy)
        {
            this.Width = width;
            this.Height = height;
            this.InitialEnergyResource = initialEnergyResource;
            this.AvalaibleEnergyResource = initialEnergyResource;
            this.MutationProbability = mutationProbability;
            this.CapEnergy = capEnergy;

            MaxEnergy = MAX_ENERGY;
            InitialEnergy = INITIAL_ENERGY;
            //InitialWidth = INITIAL_WIDTH;
            //InitialHeight = INITIAL_HEIGHT;
            Epsilon = EPSILON;
            SimulationStepInterval = SIMULATION_STEP_INTERVAL;
            EnergyIncrement = ENERGY_INCREMENT;
            //WidthIncrement = WIDTH_INCREMENT;
            //HeightIncrement = HEIGHT_INCREMENT;
            this.DetectBacteriaCollision = DETECT_BACTERIA_COLLISION;

            // Decomment only for trsting purposes on the ForceField implementation
            //this.ActiveForceField = new ForceField();
        }

        public void GrowBacterium(Bacterium bacterium)
        {
            double maxEnergyRequest = Math.Min(bacterium.EnergyIncrement, bacterium.MaxEnergy - bacterium.EnergyIncrement);
            double energyProvided = ProvideEnergy(maxEnergyRequest);
            bacterium.Energy += energyProvided;
            bacterium.adjustRadius();
        }

        public void UngrowBacterium(Bacterium bacterium)
        {
            Double maxDecrement = Math.Min(bacterium.Energy, bacterium.EnergyIncrement);
            IncreaseEnergy(maxDecrement);
            bacterium.Energy -= maxDecrement;
            Double radiusDecrement = -1 * Math.Sqrt(bacterium.Energy / Math.PI) + (bacterium.Width / 2);
            bacterium.Width -= radiusDecrement;
            bacterium.Height -= radiusDecrement;
            if (bacterium.Width <= 0)
            {
                bacterium.Width = SimulationEnvironment.EPSILON;
            }
            if (bacterium.Height <= 0)
            {
                bacterium.Height = SimulationEnvironment.EPSILON;
            }
        }

        public Double ProvideEnergy(Double energyRequested)
        {
            if (this.AvalaibleEnergyResource <= 0)
            {
                return 0;
            }
            else if (this.AvalaibleEnergyResource < energyRequested)
            {
                Double energyProvided = this.AvalaibleEnergyResource;
                this.AvalaibleEnergyResource = 0;
                return energyProvided;
            }
            else
            {
                AvalaibleEnergyResource -= energyRequested;
                return energyRequested;
            }
        }

        public void IncreaseEnergy(Double energyProvided)
        {
            if (CapEnergy && (AvalaibleEnergyResource + energyProvided) > InitialEnergyResource)
            {
                AvalaibleEnergyResource = InitialEnergyResource;
            }
            else
            {
                this.AvalaibleEnergyResource += energyProvided;
            }
        }
    }
}
